package edison.jcr;

import java.io.InputStream;
import java.math.BigDecimal;
import java.util.Calendar;

import javax.jcr.Binary;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.PropertyDefinition;
import javax.jcr.version.VersionException;

import edison.persistence.ItemService;
import edison.persistence.NodeBean;
import edison.persistence.PersistenceException;
import edison.persistence.PropertyBean;
import edison.utils.Converter;

public class PropertyImpl extends ItemImpl implements Property {

	private ItemService itemService;
	private Value value;
	private Value[] values;
	private PropertyDefinition definition;
	private int type;
	private boolean isMultiple;

	public PropertyImpl(Session session, String path) {
		super(session, path, false);
	}

	public void setValue(Value value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
		if (this.isMultiple())
			throw new ConstraintViolationException();
		if (this.getType() != value.getType())
			throw new ValueFormatException();

		this.value = VALUE_FACTORY.createValue(value.getString(), this.getType());

	}

	public void setValue(Value[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
		if (!this.isMultiple())
			throw new ConstraintViolationException();
		for (Value value : values) {
			if (this.getType() != value.getType())
				throw new ValueFormatException();
		}

		this.values = values.clone();

	}

	public void setValue(String value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
		this.setValue(VALUE_FACTORY.createValue(value));

	}

	public void setValue(String[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
		Value[] vs = new ValueImpl[values.length];

		for (int i = 0; i < values.length; i++) {
			vs[i] = VALUE_FACTORY.createValue(value.getString(), this.getType());
		}

		this.setValue(vs);

	}

	@SuppressWarnings("deprecation")
	public void setValue(InputStream value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
		this.setValue(VALUE_FACTORY.createValue(value));

	}

	public void setValue(Binary value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
		this.setValue(VALUE_FACTORY.createValue(value));

	}

	public void setValue(long value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
		this.setValue(VALUE_FACTORY.createValue(value));

	}

	public void setValue(double value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
		this.setValue(VALUE_FACTORY.createValue(value));
	}

	public void setValue(BigDecimal value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
		this.setValue(VALUE_FACTORY.createValue(value));

	}

	public void setValue(Calendar value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
		this.setValue(VALUE_FACTORY.createValue(value));

	}

	public void setValue(boolean value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
		this.setValue(VALUE_FACTORY.createValue(value));

	}

	public void setValue(Node value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
		this.setValue(VALUE_FACTORY.createValue(value));

	}

	public Value getValue() throws ValueFormatException, RepositoryException {
		if (this.isMultiple())
			throw new ValueFormatException();
		return new ValueImpl(this.value.toString(), this.value.getType());
	}

	public Value[] getValues() throws ValueFormatException, RepositoryException {
		if (!this.isMultiple())
			throw new ValueFormatException();
		return values.clone();
	}

	public String getString() throws ValueFormatException, RepositoryException {
		if (this.isMultiple())
			throw new ValueFormatException();
		return value.getString();
	}

	@SuppressWarnings("deprecation")
	public InputStream getStream() throws ValueFormatException, RepositoryException {
		if (this.isMultiple())
			throw new ValueFormatException();
		return value.getStream();
	}

	public Binary getBinary() throws ValueFormatException, RepositoryException {
		if (this.isMultiple())
			throw new ValueFormatException();
		return value.getBinary();
	}

	public long getLong() throws ValueFormatException, RepositoryException {
		if (this.isMultiple())
			throw new ValueFormatException();
		return value.getLong();
	}

	public double getDouble() throws ValueFormatException, RepositoryException {
		if (this.isMultiple())
			throw new ValueFormatException();
		return value.getLong();
	}

	public BigDecimal getDecimal() throws ValueFormatException, RepositoryException {
		if (this.isMultiple())
			throw new ValueFormatException();
		return value.getDecimal();
	}

	public Calendar getDate() throws ValueFormatException, RepositoryException {
		if (this.isMultiple())
			throw new ValueFormatException();
		return value.getDate();
	}

	public boolean getBoolean() throws ValueFormatException, RepositoryException {
		if (this.isMultiple())
			throw new ValueFormatException();
		return value.getBoolean();
	}

	public Node getNode() throws ItemNotFoundException, ValueFormatException, RepositoryException {
		if (this.isMultiple())
			throw new ValueFormatException();

		if (this.getType() != PropertyType.REFERENCE)
			throw new ValueFormatException();
		if (this.getType() != PropertyType.WEAKREFERENCE)
			throw new ValueFormatException();
		if (this.getType() != PropertyType.PATH)
			throw new ValueFormatException();

		NodeBean nodeBean;
		if (this.getType() == PropertyType.REFERENCE || this.getType() != PropertyType.WEAKREFERENCE) {
			String nodeId = value.getString();
			try {
				nodeBean = itemService.getNodeById(nodeId);
			} catch (PersistenceException e) {
				throw new RepositoryException(e);
			}
		} else {
			String absPath = value.getString();
			try {
				nodeBean = itemService.getNodeByPath(absPath);
			} catch (PersistenceException e) {
				throw new RepositoryException(e);
			}
		}

		if (nodeBean == null)
			throw new ItemNotFoundException();

		return Converter.toNode(nodeBean);
	}

	public Property getProperty() throws ItemNotFoundException, ValueFormatException, RepositoryException {
		if (this.isMultiple())
			throw new ValueFormatException();

		if (this.getType() != PropertyType.PATH)
			throw new ValueFormatException();

		PropertyBean propertyBean;
		String absPath = value.getString();

		try {
			propertyBean = itemService.getPropertyByPath(absPath);
		} catch (PersistenceException e) {
			throw new RepositoryException(e);
		}

		if (propertyBean == null)
			throw new ItemNotFoundException();

		return Converter.toProperty(propertyBean);
	}

	public long getLength() throws ValueFormatException, RepositoryException {
		if (this.isMultiple())
			throw new ValueFormatException();

		if (this.getType() == PropertyType.STRING) {
			return value.getString().length();
		}
		if (this.getType() == PropertyType.BINARY) {
			return value.getBinary().getSize();
		}

		return -1;
	}

	public long[] getLengths() throws ValueFormatException, RepositoryException {
		if (!this.isMultiple())
			throw new ValueFormatException();
		long[] lengths = new long[values.length];
		for (int i = 0; i < lengths.length; i++) {
			lengths[i] = -1;
		}

		if (this.getType() == PropertyType.STRING)
			for (int i = 0; i < values.length; i++) {
				lengths[i] = values[i].getString().length();
			}

		if (this.getType() == PropertyType.BINARY)
			for (int i = 0; i < values.length; i++) {
				lengths[i] = values[i].getBinary().getSize();
			}

		return lengths;
	}

	public PropertyDefinition getDefinition() throws RepositoryException {
		return definition;
	}

	public int getType() throws RepositoryException {
		return type;
	}

	public boolean isMultiple() throws RepositoryException {
		return isMultiple;
	}

}